1 module hip.data.json;
2 import hip.util.array;
3 
4 
5 JSONValue parseJSON(string jsonData)
6 {
7     JSONValue ret = JSONValue.parse(jsonData);
8 	if(ret.hasErrorOccurred)
9 		throw new Exception(ret.error);
10 	return ret;
11 }
12 
13 T tryGetValue(T)(JSONValue v, string prop, T defaultValue = T.init)
14 {
15 	JSONValue* ret = prop in v;
16 	if(ret)
17 	{
18 		static if(is(T == float)) return ret.floating;
19 		else static if(is(T == int)) return ret.integer;
20 		else return ret.get!T;
21 	}
22 	return defaultValue;
23 }
24 
25 alias JSONObject = JSONValue[string];
26 
27 struct JSONArray
28 {
29 	size_t length() const { return value.length; }
30 	private CacheArray!(JSONValue, 4) value;
31 
32 	/**
33 	 * Small array that holds up to N members in static memory. Whenever bigger than N,
34 	 * uses default D dynamic array.
35 	 */
36 	private static struct CacheArray(T, size_t N)
37 	{
38 		private T[N] staticData;
39 		private T[] dynData;
40 		private size_t actualLength;
41 
42 		this(T[] value)
43 		{
44 			this.set(value);
45 		}
46 
47 		private void set(T[] values)
48 		{
49 			if(values.length <= N)
50 				staticData.ptr[0..values.length] = values[];
51 			else
52 			{
53 				if(dynData is null)
54 					dynData = values.dup;
55 				else
56 				{
57 					if(dynData.length < values.length)
58 						dynData.length = values.length;
59 					dynData[0..values.length] = values[];
60 				}
61 			}
62 			actualLength = values.length;
63 		}
64 		private void append(T value)
65 		{
66 			T[] temp = (&value)[0..1];
67 			append(temp);
68 		}
69 
70 		private void append(T[] values)
71 		{
72 			import core.stdc.string;
73 			if(actualLength + values.length <= N)
74 			{
75 				memcpy(staticData.ptr + actualLength, values.ptr, values.length * T.sizeof);
76 			}
77 			else
78 			{
79 				if(dynData is null)
80 				{
81 					dynData = uninitializedArray!(T[])(actualLength+values.length);
82 					memcpy(dynData.ptr, staticData.ptr, actualLength * T.sizeof);
83 				}
84 				else if (dynData.length < actualLength + values.length)
85 				{
86 					size_t newSize = actualLength + values.length;
87 
88 					version(WebAssembly)
89 					{
90 						//TODO: Needs to fix somewhere in the walloc allocator. Realloc is buggy currently
91 						T[] newDyn = new T[newSize];
92 						newDyn[0..actualLength] = dynData[0..actualLength];
93 						dynData = newDyn;
94 					}
95 					else
96 						dynData.length = newSize;
97 				}
98 				memcpy(dynData.ptr + actualLength, values.ptr, values.length * T.sizeof);
99 			}
100 			actualLength+= values.length;
101 		}
102 
103 		void trim()
104 		{
105 			if(dynData !is null)
106 				dynData.length = actualLength;
107 		}
108 		size_t length() const { return actualLength; }
109 		inout(T)[] getArray() inout
110 		{
111 			if(dynData !is null)
112 				return dynData[0..actualLength];
113 			return staticData[0..actualLength];
114 		}
115 	}
116 
117 	this(JSONValue[] v)
118 	{
119 		this.value = CacheArray!(JSONValue, 4)(v);
120 	}
121 
122 	static JSONArray* append(JSONArray* self, JSONValue v)
123 	{
124 		self.value.append(v);
125 		return self;
126 	}
127 	auto opOpAssign(string op, T)(T value) if(op == "~")
128 	{
129 		static if(is(T == JSONValue))
130 			append(&this, value);
131 		else
132 			append(&this, JSONValue(value));
133 		return this;
134 	}
135 	private static JSONArray* trim(JSONArray* self)
136 	{
137 		self.value.trim();
138 		return self;
139 	}
140 
141 	static JSONArray* createNew()
142 	{
143 		return new JSONArray([]);
144 	}
145 
146 	static JSONArray* createNew(JSONValue[] data)
147 	{
148 		JSONArray* ret = new JSONArray(data);
149 		return ret;
150 	}
151 
152 	JSONValue[] getArray(){return value.getArray;}
153 	const(JSONValue)[] getArray() const {return value.getArray;}
154 }
155 
156 private enum JSONState
157 {
158 	key,
159 	lookingAssignment,
160 	lookingForNext,
161 	value
162 }
163 
164 enum JSONType : ubyte
165 {
166 	bool_ = 0,
167 	float_ = 1,
168 	int_ = 2, integer = int_, uinteger = int_,
169 	string_ = 3, string = string_,
170 	array = 4,
171 	object = 5,
172 	error = 6,
173 	null_ = 7 //0b111
174 }
175 
176 pragma(inline, true)
177 bool isWhitespace(char ch)
178 {
179 	switch(ch)
180 	{
181 		case ' ', '\t', '\n', '\r': return true;
182 		default: return false;
183 	}
184 }
185 
186 pragma(inline, true) bool isNumber(char ch){return '0' <= ch && ch <= '9';}
187 pragma(inline, true) bool isNumeric(char ch){return ('0' <= ch  && ch <= '9') || ch == '-' || ch == '.';}
188 
189 private union JSONData
190 {
191 	double _float;
192 	long _int;
193 	bool _bool;
194 	immutable(char)* _string;
195 	JSONObject object;
196 	JSONArray* array;
197 }
198 
199 
200 struct JSONValue
201 {
202 	JSONData data;
203 	static if(size_t.sizeof == uint.sizeof)
204 	{
205 		///Used only for the string.
206 		uint _length;
207 
208 		pragma(inline, true) JSONType type(JSONType t)
209 		{
210 			_length = (_length & 0x1FFFFFFF) | (cast(uint)t << 29);
211 			return t;
212 		}
213 		pragma(inline, true) JSONType type() const
214 		{
215 			return cast(JSONType)(_length >> 29);
216 		}
217 
218 		pragma(inline, true)  private void setString(string s)
219 		{
220 			import core.stdc.string;
221 			data._string = s.ptr;
222 			_length = (s.length & 0x1FFFFFFF) | (cast(uint)type << 29);
223 		}
224 
225 		pragma(inline, true) private uint length() const
226 		{
227 			return _length & 0x1FFFFFFF;
228 		}
229 	}
230 	else
231 	{
232 		///Used only for the string.
233 		ulong _length;
234 
235 		pragma(inline, true) JSONType type(JSONType t)
236 		{
237 			_length = (_length & 0x1FFFFFFFFFFFFFFF) | (cast(ulong)t << 61);
238 			return t;
239 		}
240 		pragma(inline, true) JSONType type() const
241 		{
242 			return cast(JSONType)(_length >> 61);
243 		}
244 
245 		pragma(inline, true)  private void setString(string s)
246 		{
247 			import core.stdc.string;
248 			data._string = s.ptr;
249 			_length = (s.length & 0x1FFFFFFFFFFFFFFF) | (cast(ulong)type << 61);
250 		}
251 
252 		pragma(inline, true) private ulong length() const
253 		{
254 			return _length & 0x1FFFFFFFFFFFFFFF;
255 		}
256 	}
257 
258 
259 	this(T)(T value)
260 	{
261 		import std.traits;
262 		static if(isIntegral!T)
263 		{
264 			type = JSONType.int_;
265 			data._int = value;
266 		}
267 		else static if(isFloatingPoint!T)
268 		{
269 			type = JSONType.float_;
270 			data._float = value;
271 		}
272 		else static if(is(T == bool))
273 		{
274 			type = JSONType.bool_;
275 			data._bool = value;
276 		}
277 		else static if(is(T == string))
278 		{
279 			type = JSONType.string_;
280 			setString(value);
281 		}
282 		else static if(is(T == JSONObject))
283 		{
284 			type = JSONType.object;
285 			data.object = value;
286 		}
287 		else static if(is(T == JSONArray*))
288 		{
289 			type = JSONType.array;
290 			data.array = value;
291 		}
292 		else static if(is(T == JSONValue[]))
293 		{
294 			data.array = JSONArray.createNew(value);
295 			type = JSONType.array;
296 		}
297 		else static if(is(T == JSONValue))
298 		{
299 			type = value.type;
300 			data = value.data;
301 		}
302 		else static if(is(T == typeof(null)))
303 		{
304 			data.object = null;
305 			type = JSONType.null_;
306 		}
307 		else static assert(false, "Unsupported type ", T.stringof);
308 	}
309 	pragma(inline, false)
310 	{
311 		int integer() const
312 		{
313 			return type == JSONType.integer ? get!int : cast(int)get!float;
314 		}
315 		float floating() const
316 		{
317 			return type == JSONType.float_ ? get!float : cast(float)get!int;
318 		}
319 	}
320 
321 	pragma(inline, true)
322 	{
323 		bool boolean() const {return get!bool;}
324 		string str() const {return get!string;}
325 		string error() const{return get!string;}
326 	}
327 
328     ///Returns an array range.
329     auto array() const
330     {
331 		import hip.util.exception;
332         enforce(type == JSONType.array, "Tried to iterate a non array object of type "~getTypeName);
333         struct JSONValueArrayIterator
334         {
335             private const(JSONArray*) arr;
336             private size_t idx = 0;
337             size_t length(){return arr.length;}
338             bool empty(){return idx == arr.length;}
339             void popFront(){idx++;}
340             const(JSONValue) front(){return arr.getArray()[idx];}
341             const(JSONValue) opIndex(size_t num){return arr.getArray()[num];}
342         }
343         return JSONValueArrayIterator(data.array);
344     }
345 
346 	ref JSONArray jsonArray()
347 	{
348 		return *data.array;
349 	}
350 
351 	JSONValue[] array()
352 	{
353 		import hip.util.exception;
354 		enforce(type == JSONType.array, "Tried to iterate a non array object of type "~getTypeName);
355 		return data.array.getArray();
356 	}
357 
358     JSONValue object() const
359     {
360 		import hip.util.exception;
361         enforce(type == JSONType.object, "Tried to get type object but value is of type "~getTypeName);
362 		JSONValue ret;
363 		ret.data = data;
364 		ret.type = JSONType.object;
365         return ret;
366     }
367 
368 	string[] keys()
369 	{
370 		import hip.util.exception;
371         enforce(type == JSONType.object, "Tried to get type object but value is of type "~getTypeName);
372 		return data.object.keys;
373 	}
374 	JSONValue[] values()
375 	{
376 		import hip.util.exception;
377         enforce(type == JSONType.object, "Tried to get type object but value is of type "~getTypeName);
378 		return data.object.values;
379 	}
380 
381 	string getTypeName() const
382 	{
383 		final switch(type) with(JSONType)
384 		{
385 			case int_: return "int";
386 			case bool_: return "bool";
387 			case float_: return "float";
388 			case string_: return "string";
389 			case object: return "object";
390 			case array: return "array";
391 			case error: return "error";
392 			case null_: return "null";
393 		}
394 	}
395 
396 	T get(T)() const
397 	{
398 		import std.traits;
399 		import hip.util.exception;
400 		static if(isIntegral!T)
401 		{
402 			enforce(type == JSONType.int_, "Tried to get type "~T.stringof~" but value is of type "~getTypeName);
403 			return cast(Unqual!T)data._int;
404 		}
405 		else static if(isFloatingPoint!T)
406 		{
407 			enforce(type == JSONType.float_, "Tried to get type "~T.stringof~" but value is of type "~getTypeName);
408 			return cast(Unqual!T)data._float;
409 		}
410 		else static if(is(T == bool))
411 		{
412 			enforce(type == JSONType.bool_, "Tried to get type "~T.stringof~" but value is of type "~getTypeName);
413 			return cast(Unqual!T)data._bool;
414 		}
415 		else static if(is(T == string))
416 		{
417 			enforce(type == JSONType.string_ || type == JSONType.error, "Tried to get type "~T.stringof~" but value is of type "~getTypeName);
418 			return data._string[0..cast(size_t)length];
419 		}
420 		else static if(is(T == JSONObject))
421 		{
422 			enforce(type == JSONType.object, "Tried to get type "~T.stringof~" but value is of type "~getTypeName);
423 			return data.object;
424 		}
425 		else static if(is(T == JSONArray))
426 		{
427 			enforce(type == JSONType.array, "Tried to get type "~T.stringof~" but value is of type "~getTypeName);
428 			return *data.array;
429 		}
430 	}
431 	bool isNull()
432 	{
433 		with(JSONType)
434 		{
435 			if(type == null_) return true;
436 			if(type == JSONType.object) return data.object == null;
437 			if(type == JSONType.array) return data.array == null;
438 		}
439 		return false;
440 	}
441 
442 	static JSONValue emptyObject()
443 	{
444 		JSONValue ret;
445 		ret.type = JSONType.object;
446 		ret.data.object = new JSONObject();
447 		return ret;
448 	}
449 	static JSONValue emptyArray()
450 	{
451 		JSONValue ret;
452 		ret.type = JSONType.array;
453 		ret.data.array = JSONArray.createNew();
454 		return ret;
455 	}
456 
457 	private static JSONValue create(T)(T data)
458 	{
459 		JSONValue ret = JSONValue(data);
460 		return ret;
461 	}
462 	private static JSONValue errorObj(string message)
463 	{
464 		JSONValue ret;
465 		ret.setString(message);
466 		ret.type = JSONType.error;
467 		return ret;
468 	}
469 
470 
471 	private static JSONValue parse(string data)
472 	{
473 		import core.memory;
474 		import hip.util.conv:to;
475 
476 		if(!data.length)
477 		{
478 			return JSONValue.errorObj("No data provided");
479 		}
480 		ptrdiff_t index = 0;
481 		StringPool pool = StringPool(cast(size_t)(data.length*0.75));
482 
483 		bool getNextString(string data, ptrdiff_t currentIndex, out ptrdiff_t newIndex, out string theString)
484 		{
485 			assert(data[currentIndex] == '"', "getNextString must start with a quotation mark");
486 			ptrdiff_t i = currentIndex + 1;
487 			size_t returnLength = 0;
488 			char[] ret = pool.getNewString(64);
489 			char ch;
490 
491 			while(i < data.length)
492 			{
493 				ch = data[i];
494 				switch(ch)
495 				{
496 					case '"':
497 						ret = pool.resizeString(ret, returnLength);
498 						newIndex = i;
499 						theString = cast(string)ret;
500 						return true;
501 					case '\\':
502 						if(i + 1 >= data.length)
503 							return false;
504 						ch = escapedCharacter(data[++i]);
505 						break;
506 					default: break;
507 
508 				}
509 				if(returnLength >= ret.length)
510 					ret = pool.resizeString(ret, ret.length*2);
511 
512 				ret[returnLength++] = ch;
513 				i++;
514 			}
515 			return false;
516 		}
517 
518 
519 		bool getNextNumber(string data, ptrdiff_t currentIndex, out ptrdiff_t newIndex, out JSONData theData, out JSONType type)
520 		{
521 			assert(data[currentIndex].isNumeric);
522 			bool hasDecimal = false;
523 			newIndex = currentIndex;
524 			if(data[currentIndex] == '-')
525 				newIndex++;
526 
527 			while(newIndex < data.length)
528 			{
529 				if(!hasDecimal && data[newIndex] == '.')
530 				{
531 					hasDecimal = true;
532 					if(newIndex+1 < data.length) newIndex++;
533 				}
534 				if(!isNumber(data[newIndex]))
535 					break;
536 				newIndex++;
537 			}
538 			if(hasDecimal)
539 			{
540 				theData._float = to!double(data[currentIndex..newIndex]);
541 				type = JSONType.float_;
542 			}
543 			else
544 			{
545 				theData._int = to!long(data[currentIndex..newIndex]);
546 				type = JSONType.int_;
547 			}
548 			//Stopped on a non number. Revert 1 step.
549 			newIndex--;
550 			return newIndex < data.length;
551 		}
552 		JSONValue ret;
553 		ret.type = JSONType.null_;
554 		JSONValue* current = &ret;
555 		JSONState state = JSONState.value;
556 		JSONValue lastValue = ret;
557 
558 		scope JSONValue[] stack = uninitializedArray!(JSONValue[])(32);
559 		scope ptrdiff_t stackLength = 0;
560 
561 		size_t line = 0;
562 		string getErr(string err="", string f = __FILE_FULL_PATH__, size_t l = __LINE__)
563 		{
564 			return "Error at line "~line.to!string~" "~err~" on index '"~index.to!string~"' last parsed: "~lastValue.toString~" [Internal: "~f~":"~l.to!string~"]";
565 		}
566 
567 		string lastKey;
568 		do
569 		{
570 			char ch = data[index];
571 			switch(ch)
572 			{
573 				case '\n':
574 					line++;
575 					break;
576 				case '{':
577 				{
578 					if(state != JSONState.value)
579 						return JSONValue.errorObj(getErr());
580 					JSONValue obj = JSONValue.create(new JSONObject);
581 					if(!pushNewScope(obj, current, stackLength, stack, lastKey))
582 						return JSONValue.errorObj(getErr("Could not push new scope in JSON. Only array, object or null are valid"));
583 
584 					state = JSONState.key;
585 					break;
586 				}
587 				case '}':
588 					popScope(stackLength, stack, current);
589 					state = JSONState.lookingForNext;
590 					break;
591 				case ':':
592 					if(state != JSONState.lookingAssignment)
593 						return JSONValue.errorObj(getErr("expected key before ':'"));
594 					state = JSONState.value;
595 					break;
596 				case '"':
597 				{
598 
599 					switch(state)
600 					{
601 						case JSONState.lookingForNext:
602 							if(current.type == JSONType.object)
603 								goto case JSONState.key;
604 							else if(current.type == JSONType.array)
605 								goto case JSONState.value;
606 							goto default;
607 						case JSONState.key:
608 						{
609 							assert(current.type == JSONType.object, getErr("only object can receive a key."));
610 							if(!getNextString(data, index, index, lastKey))
611 								return JSONValue.errorObj(getErr("unclosed quotes."));
612 							state = JSONState.lookingAssignment;
613 							break;
614 						}
615 						case JSONState.value:
616 						{
617 							string val;
618 							if(!getNextString(data, index, index, val))
619 								return JSONValue.errorObj(getErr("unclosed quotes."));
620 							pushToStack(JSONValue(val), current, lastValue, lastKey);
621 							state = JSONState.lookingForNext;
622 							break;
623 						}
624 						default:
625 							return JSONValue.errorObj(getErr("comma expected before key "~lastKey));
626 					}
627 					break;
628 				}
629 				case '[':
630 				{
631 					if(state != JSONState.lookingForNext && state != JSONState.value)
632 						return JSONValue.errorObj(getErr(" expected to be a value. "));
633 					if(!pushNewScope(JSONValue(JSONArray.createNew()), current, stackLength, stack, lastKey))
634 						return JSONValue.errorObj(getErr("Could not push new scope in JSON. Only array, object or null are valid"));
635 					state = JSONState.value;
636 					break;
637 				}
638 				case ']':
639 					if(state != JSONState.lookingForNext && state != JSONState.value)
640 						return JSONValue.errorObj(getErr("expected to be a value. "));
641 					popScope(stackLength, stack, current);
642 					state = JSONState.lookingForNext;
643 					break;
644 				case ',':
645 					if(state != JSONState.lookingForNext)
646 						return JSONValue.errorObj(getErr("unexpected comma. "));
647 					if(current.type != JSONType.object && current.type != JSONType.array)
648 						return JSONValue.errorObj(getErr("unexpected comma. "));
649 
650 					switch(current.type) with(JSONType)
651 					{
652 						case object: state = JSONState.key; break;
653 						case array: state = JSONState.value; break;
654 						default: assert(false, "Error?");
655 					}
656 					break;
657 				default:
658 					switch(state)
659 					{
660 						case JSONState.value: //Any value
661 						case JSONState.lookingForNext: //Array
662 							if(ch.isNumeric)
663 							{
664 								if(state == JSONState.lookingForNext && current.type != JSONType.array)
665 									return JSONValue.errorObj(getErr("unexpected number."));
666 								JSONType out_type;
667 								if(!getNextNumber(data, index, index, lastValue.data, out_type))
668 									return JSONValue.errorObj(getErr("unexpected end of file."));
669 								lastValue.type = out_type;
670 								pushToStack(lastValue, current, lastValue, lastKey);
671 								state = JSONState.lookingForNext;
672 							}
673 							else if(index + "true".length < data.length && data[index.."true".length + index] == "true")
674 							{
675 								if(state == JSONState.lookingForNext && current.type != JSONType.array)
676 									return JSONValue.errorObj(getErr("unexpected number."));
677 								pushToStack(JSONValue(true), current, lastValue, lastKey);
678 								state = JSONState.lookingForNext;
679 							}
680 							else if(index + "false".length < data.length && data[index.."false".length + index] == "false")
681 							{
682 								if(state == JSONState.lookingForNext && current.type != JSONType.array)
683 									return JSONValue.errorObj(getErr("unexpected number."));
684 								pushToStack(JSONValue(false), current, lastValue, lastKey);
685 								state = JSONState.lookingForNext;
686 							}
687 							else if(index + "null".length < data.length && data[index.."null".length + index] == "null")
688 							{
689 								if(state == JSONState.lookingForNext && current.type != JSONType.array)
690 									return JSONValue.errorObj(getErr("unexpected number."));
691 								pushToStack(JSONValue(null), current, lastValue, lastKey);
692 								state = JSONState.lookingForNext;
693 							}
694 							break;
695 						default:break;
696 					}
697 					break;
698 			}
699 			index++;
700 		}
701 		while(index < data.length && stack.length > 0);
702 		return ret;
703 	}
704 
705 	const(JSONValue) opIndex(string key) const
706 	{
707 		assert(type == JSONType.object, "Can't get a member from a non object.");
708 		return data.object[key];
709 	}
710 	JSONValue opIndex(string key)
711 	{
712 		assert(type == JSONType.object, "Can't get a member from a non object.");
713 		return data.object[key];
714 	}
715 	JSONValue opIndexAssign(JSONValue v, string key)
716 	{
717 		import hip.util.exception;
718 		enforce(type == JSONType.object, "Can't get a member from a non object.");
719 		enforce(data.object !is null, "Can't access a null object");
720 		data.object[key] = v;
721 		return data.object[key];
722 	}
723 
724 	JSONValue opIndexAssign(T)(T value, string key) if(!is(T == JSONValue))
725 	{
726 		return opIndexAssign(JSONValue(value), key);
727 	}
728 
729 	const(JSONValue)* opBinaryRight(string op)(string key) const
730 	if(op == "in")
731 	{
732 		if(type != JSONType.object)	return null;
733 		return key in data.object;
734 	}
735     JSONValue* opBinaryRight(string op)(string key)
736 	if(op == "in")
737 	{
738 		if(type != JSONType.object)	return null;
739 		return key in data.object;
740 	}
741 
742     int opApply(scope int delegate(string key, JSONValue v) dg)
743     {
744         if(type != JSONType.object)
745         {
746             assert(false, "Can't iterate with key[string] and value[JSONValue] an object of type "~getTypeName);
747         }
748         int result = 0;
749         foreach (k, v ; data.object)
750         {
751             result = dg(k, v);
752             if (result)
753                 break;
754         }
755     
756         return result;
757     }
758 	bool hasErrorOccurred() const { return type == JSONType.error; }
759 
760 	/**
761 	 *
762 	 * Params:
763 	 *   compressed = Won't include any space in the file. Also, won't escape backslash. Since the parsing works with a single backslash.
764 	 * this may reduce the json size, and increase the parsing speed.
765 	 *   selfPrintkey = Only used for objects.
766 	 * Returns:
767 	 */
768 	string toString(bool compressed = false)() const
769 	{
770 		if(type == JSONType.error)
771 			return error();
772 		import hip.util.conv:to;
773 		string ret;
774 
775 		static string escapeCharacters(string input)
776 		{
777 			size_t length = input.length;
778 			foreach(ch; input)
779 			{
780 				if(ch == '\n' || ch == '\t' || ch == '\r' || ch == '"' || ch == '\\') length++;
781 			}
782 			if(length == input.length) return input;
783 			char[] escaped = new char[](length);
784 			length = 0;
785 			foreach(i; 0..input.length)
786 			{
787 				switch(input[i])
788 				{
789 					case '"':
790 						escaped[length] = '\\';
791 						escaped[++length] = '"';
792 						break;
793 					case '\\':
794 						escaped[length] = '\\';
795 						escaped[++length] = '\\';
796 						break;
797 					case '\n':
798 						escaped[length] = '\\';
799 						escaped[++length] = 'n';
800 						break;
801 					case '\r':
802 						escaped[length] = '\\';
803 						escaped[++length] = 'r';
804 						break;
805 					case '\t':
806 						escaped[length] = '\\';
807 						escaped[++length] = 't';
808 						break;
809 					default:
810 						escaped[length] = input[i];
811 						break;
812 				}
813 				length++;
814 			}
815 			return cast(string)escaped;
816 		}
817 
818 		final switch ( type )
819 		{
820 			case JSONType.int_:
821 				ret = data._int.to!(string);
822 				break;
823 			case JSONType.float_:
824 				ret = data._float.to!string;
825 				break;
826 			case JSONType.bool_:
827 				ret = data._bool ? "true" : "false";
828 				break;
829 			case JSONType.error:
830 				ret = error();
831 				break;
832 			case JSONType.string_:
833 				ret = '"'~escapeCharacters(get!string)~'"';
834 				break;
835 			case JSONType.null_:
836 				ret = "null";
837 				break;
838 			case JSONType.array:
839 			{
840 				ret = "[";
841 				bool isFirst = true;
842 				foreach(v; data.array.getArray)
843 				{
844 					static if(compressed)
845 					{
846 						if(!isFirst)
847 							ret~= ',';
848 					}
849 					else
850 					{
851 						if(!isFirst)
852 							ret~= ", ";
853 					}
854 					isFirst = false;
855 					ret~= v.toString!compressed();
856 				}
857 				ret~= "]";
858 				break;
859 			}
860 			case JSONType.object:
861 			{
862 
863 				ret~= '{';
864 				bool isFirst = true;
865 				foreach(k, v; data.object)
866 				{
867 					static if(compressed)
868 					{
869 						if(!isFirst)
870 							ret~= ',';
871 					}
872 
873 					else
874 					{
875 						if(!isFirst)
876 							ret~=  ", ";
877 					}
878 					isFirst = false;
879 					static if(compressed)
880 						ret~= '"'~escapeCharacters(k)~"\":"~v.toString!compressed;
881 					else
882 						ret~= '"'~escapeCharacters(k)~"\" : "~v.toString!compressed;
883 				}
884 				ret~= '}';
885 				break;
886 
887 			}
888 		}
889 		return ret;
890 	}
891 
892     void dispose()
893     {
894         if(type == JSONType.object)
895         {
896             foreach(v; data.object)
897                 v.dispose();
898         }
899         else if(type == JSONType.array)
900         {
901             foreach(v; data.array.getArray)
902                 v.dispose();
903         }
904         
905     }
906 }
907 
908 private struct StringPool
909 {
910 	private char[] pool;
911 	private size_t used;
912 
913 	this(size_t size)
914 	{
915 		this.pool = uninitializedArray!(char[])(size);
916 	}
917 
918 	bool getSlice(size_t sliceSize, out char[] str)
919 	{
920 		if(used+sliceSize < pool.length)
921 		{
922 			str = pool[used..used+sliceSize];
923 			used+= sliceSize;
924 			return true;
925 		}
926 		return false;
927 	}
928 
929 	char[] resizeString(char[] str, size_t newSize)
930 	{
931 		///Inside pool
932 		if(newSize == str.length) return str;
933 		if(pool.ptr <= str.ptr && pool.ptr + pool.length > str.ptr)
934 		{
935 			if(newSize > str.length)
936 			{
937 				if(newSize - str.length + used > pool.length)
938 				{
939 					used-= str.length;
940 					char[] ret = uninitializedArray!(char[])(newSize);
941 					ret[0..str.length] = str[];
942 					return ret;
943 				}
944 				else
945 				{
946 					ptrdiff_t offset = str.ptr - pool.ptr;
947 					assert(offset >= 0, " Out of bounds?");
948 					used+= newSize - str.length;
949 					return pool[cast(size_t)offset..offset+newSize];
950 				}
951 			}
952 			else
953 			{
954 				used-= str.length - newSize;
955 				return str[0..newSize];
956 			}
957 		}
958 		else
959 			str.length = newSize;
960 		return str;
961 	}
962 
963 	/**
964 	*	If the pool is not enough, it will allocate randomly
965 	*/
966 	char[] getNewString(size_t strSize)
967 	{
968 		char[] ret;
969 		if(getSlice(strSize, ret))
970 			return ret;
971 		return new char[](strSize);
972 	}
973 }
974 
975 pragma(inline, true)
976 bool pushNewScope(JSONValue val, ref JSONValue* current, ref ptrdiff_t stackLength, ref JSONValue[] stack, string key)
977 {
978 	assert(val.type == JSONType.object || val.type == JSONType.array || val.type == JSONType.null_, "Unexpected push.");
979 	JSONValue* currTemp = current;
980 
981 	stackLength++;
982 	if(stackLength > stack.length)
983 		stack~= val;
984 	else
985 		stack[stackLength-1] = val;
986 
987 	current = &stack[stackLength-1];
988 
989 
990 	switch(currTemp.type)
991 	{
992 		case JSONType.object:
993 			currTemp.data.object[key] = *current;
994 			break;
995 		case JSONType.array:
996 			currTemp.data.array = JSONArray.append(currTemp.data.array, *current);
997 			break;
998 		case JSONType.null_:
999 			currTemp.type = val.type;
1000 			currTemp.data = val.data;
1001 			break;
1002 		default: return false;
1003 	}
1004 	return true;
1005 }
1006 
1007 
1008 pragma(inline, true)
1009 void popScope(ref ptrdiff_t stackLength, ref JSONValue[] stack, ref JSONValue* current)
1010 {
1011 	assert(stackLength > 0, "Unexpected pop.");
1012 
1013 	stackLength--;
1014 	if(stackLength > 0)
1015 	{
1016 		JSONValue* next = &stack[stackLength-1];
1017 		if(current.type == JSONType.array)
1018 			current.data.array = JSONArray.trim(current.data.array);
1019 		current = next;
1020 		import hip.util.conv;
1021 		assert(current.type == JSONType.object || current.type == JSONType.array, "Unexpected value in stack. (Typed "~(cast(size_t)(current.type)).to!string);
1022 	}
1023 }
1024 
1025 pragma(inline, true)
1026 void pushToStack(JSONValue val, ref JSONValue* current, ref JSONValue lastValue, string lastKey)
1027 {
1028 	switch(current.type) with(JSONType)
1029 	{
1030 		case object:
1031 			current.data.object[lastKey] = val;
1032 			break;
1033 		case array:
1034 			current.data.array = JSONArray.append(current.data.array, val);
1035 			break;
1036 		case null_:
1037 			*current = val;
1038 			break;
1039 		default: assert(false, "Unexpected stack type: "~current.getTypeName);
1040 	}
1041 	lastValue = val;
1042 }
1043 
1044 
1045 unittest
1046 {
1047 	assert(parseJSON(`
1048 	{
1049     "name": "redub",
1050     "description": "Dub Based Build System, with parallelization per packages and easier to contribute",
1051     "authors": ["Hipreme"],
1052     "targetPath": "build",
1053     "buildOptions": [
1054         "debugInfo",
1055         "debugInfoC",
1056         "debugMode"
1057     ],
1058     "configurations": [
1059         {
1060             "name": "cli",
1061             "targetType": "executable"
1062         },
1063         {
1064             "name": "library",
1065             "targetType": "staticLibrary",
1066             "excludedSourceFiles": ["source/app.d"]
1067         }
1068     ],
1069     "license": "MIT",
1070     "dependencies": {
1071         "semver": {"path": "semver"},
1072         "colorize": {"path": "colorize"},
1073         "adv_diff": {"path": "adv_diff"},
1074         "hipjson": {"path": "hipjson"},
1075         "xxhash3": "~>0.0.5"
1076     }
1077 
1078 }`).object["configurations"].array.length == 2);
1079 
1080 }
1081 
1082 unittest
1083 {
1084 	enum json = `
1085 {
1086     "D5F04185E96CC720": [
1087         [
1088 			"First Value"
1089         ],
1090         [
1091 			"Second Value"
1092         ]
1093     ]
1094 }`;
1095 	assert(parseJSON(json)["D5F04185E96CC720"].array[1].array[0].toString == `"Second Value"`);
1096 }
1097 
1098 
1099 pragma(inline, true)
1100 private char escapedCharacter(char a)
1101 {
1102 	switch(a)
1103 	{
1104 		case 'n': return '\n';
1105 		case 't': return '\t';
1106 		case 'b': return '\b';
1107 		case 'r': return '\r';
1108 		default: return a;
1109 	}
1110 }